/**
 * @author: 子龙
 */
package com.fzl.lotterysystem.service.impl;

import com.fzl.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.fzl.lotterysystem.common.exception.ServiceException;
import com.fzl.lotterysystem.common.utils.JacksonUtil;
import com.fzl.lotterysystem.common.utils.RedisUtil;
import com.fzl.lotterysystem.controller.param.CreateActivityParam;
import com.fzl.lotterysystem.controller.param.CreatePrizeByActivityParam;
import com.fzl.lotterysystem.controller.param.CreateUserByActivityParam;
import com.fzl.lotterysystem.controller.param.PageParam;
import com.fzl.lotterysystem.dao.dataobject.*;
import com.fzl.lotterysystem.dao.mapper.*;
import com.fzl.lotterysystem.service.ActivityService;
import com.fzl.lotterysystem.service.DTO.ActivityDTO;
import com.fzl.lotterysystem.service.DTO.ActivityDetailDTO;
import com.fzl.lotterysystem.service.DTO.CreateActivityDTO;
import com.fzl.lotterysystem.service.DTO.PageListDTO;
import com.fzl.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.fzl.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.fzl.lotterysystem.service.enums.ActivityStatusEnum;
import com.fzl.lotterysystem.service.enums.ActivityUserStatusEnum;
import jdk.jfr.Threshold;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class ActivityServiceImpl implements ActivityService {

    //活动缓存前缀
    private final String ACTIVITY_PREFIX="ACTIVITY_";
    //活动缓存过期时间
    private final Long ACTIVITY_TIMEOUT=60*60*24*3L;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private RedisUtil redisUtil;


    /**
     * 创建活动
     * @param param
     * @return
     */

    @Override
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验活动信息是否正确
        checkActivityInfo(param);
        //保存活动信息
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        //保存活动关联奖品信息
        List<CreatePrizeByActivityParam> prizeParams=param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList=prizeParams.stream()
                .map(prizeParam ->{
                    ActivityPrizeDO activityPrizeDO=new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(prizeParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(prizeParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(prizeParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);

        //保存活动关联人员信息
        List<CreateUserByActivityParam> userParams=param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList=userParams.stream()
                .map(userParam->{
                    ActivityUserDO activityUserDO=new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(userParam.getUserId());
                    activityUserDO.setUserName(userParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(activityUserDOList);

        //整合存放redis





        //activityId：ActivityDetailDTO:活动+人员+奖品
        //先获取奖品id
        List<Long> prizeIds=param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        //奖品id获取奖品基本属性列表
        List<PrizeDO> prizeDOList=prizeMapper.batchSelectByIds(prizeIds);

        ActivityDetailDTO activityDetailDTO= converToActivityDetailDTO (activityDO,
                                                                        activityUserDOList,
                                                                        prizeDOList,
                                                                        activityPrizeDOList);


        cacheActivity(activityDetailDTO);


        //构造返回
        CreateActivityDTO createActivityDTO=new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }


    /**
     * 获取活动
     * @param pageParam
     * @return
     */
    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam pageParam) {
        // 获取总量
        int total = activityMapper.count();

        // 获取当前页列表
        List<ActivityDO> activityDOList = activityMapper.selectActivityList(pageParam.offset(), pageParam.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());
        return new PageListDTO<>(total, activityDTOList);
    }

    /**
     * 获取活动详情属性
     * @param activityId
     * @return
     */
    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(activityId==null){
            log.warn("查询详细信息activityId={}",activityId);
            return null;
        }
        //查redis
        ActivityDetailDTO activityDetailDTO=getActivityFromCache(activityId);
        if(activityDetailDTO!=null){
            log.info("活动详细信息activityDetailDTO={}",JacksonUtil.writeValueAsString(activityDetailDTO));
            return activityDetailDTO;
        }

        //没查到redis数据，则查mysql数据表

        //活动表
        ActivityDO aDO=activityMapper.selectById(activityId);

        //活动奖品表
        List<ActivityPrizeDO> aPDOList=activityPrizeMapper.selectByActivityId(activityId);

        //活动人员表
        List<ActivityUserDO> aUDOList=activityUserMapper.selectByActivityId(activityId);

        //奖品表:先获取奖品idlist 才可以去查询
        List<Long> prizeIds=aPDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList=prizeMapper.batchSelectByIds(prizeIds);

        //整合数据，存放redis
        activityDetailDTO= converToActivityDetailDTO(aDO,aUDOList,pDOList,aPDOList);
        cacheActivity(activityDetailDTO);
        //构造返回
        return activityDetailDTO;
    }

    /**
     * 先拿表数据在缓存
     * @param activityId
     */
    @Override
    public void cacheActivity(Long activityId) {
        if(activityId==null){
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //查询表数据，活动表，活动奖品，活动人员，奖品
        //整合完整并缓存
        //没查到redis数据，则查mysql数据表
        //活动表
        ActivityDO aDO=activityMapper.selectById(activityId);
        if(aDO==null){
            log.error("aDO为空,要缓存的活动id有误"+aDO);
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }

        //活动奖品表
        List<ActivityPrizeDO> aPDOList=activityPrizeMapper.selectByActivityId(activityId);

        //活动人员表
        List<ActivityUserDO> aUDOList=activityUserMapper.selectByActivityId(activityId);

        //奖品表:先获取奖品idlist 才可以去查询
        List<Long> prizeIds=aPDOList.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDOList=prizeMapper.batchSelectByIds(prizeIds);

        //整合数据，存放redis
        cacheActivity(converToActivityDetailDTO(aDO,aUDOList,pDOList,aPDOList));


    }

    /**
     * 缓存完整的活动
     * @param activityDetailDTO
     */
    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
        //key:ACTIVITY_activityId  value :ActivityDetailDTO（json）
        if(activityDetailDTO==null || activityDetailDTO.getActivityId()==null){
            log.warn("缓存数据不存在");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX+activityDetailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(activityDetailDTO),
                    ACTIVITY_TIMEOUT);
        }catch (Exception e){
            log.error("缓存活动异常ActivityDetailDTO={}"
                    ,JacksonUtil.writeValueAsString(activityDetailDTO)
                    ,e);
        }
    }

    /**
     * 根据id从缓存获取活动
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId) {
        if(activityId==null ){
            log.warn("获取缓存活动数据activityId为空");
            return null;
        }
        try {
            String str=redisUtil.get(ACTIVITY_PREFIX+activityId);
            if(!StringUtils.hasText(str)){
                log.warn("获取缓存活动数据key为空"+activityId);
                return null;
            }
            return JacksonUtil.readValue(str,ActivityDetailDTO.class);

        }catch (Exception e){
            log.error("从缓存中获取活动异常activityId={}",activityId,e);
            return null;
        }

    }

    /**
     * 整合信息
     * @param activityDO
     * @param activityUserDOList
     * @param prizeDOList
     * @param activityPrizeDOList
     * @return
     */
    private ActivityDetailDTO converToActivityDetailDTO(ActivityDO activityDO,
                                                        List<ActivityUserDO> activityUserDOList,
                                                        List<PrizeDO> prizeDOList,
                                                        List<ActivityPrizeDO> activityPrizeDOList) {
        ActivityDetailDTO activityDetailDTO=new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
        //apDo prizeid,amount,status list
        //pdo prizeid,anme... list
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList=activityPrizeDOList
                .stream()
                .map(apDO -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO=new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(apDO.getPrizeId());
                   Optional<PrizeDO> optionalPrizeDO= prizeDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                   optionalPrizeDO.ifPresent(prizeDO -> {
                       prizeDTO.setName(prizeDO.getName());
                       prizeDTO.setImageUrl(prizeDO.getImageUrl());
                       prizeDTO.setPrice(prizeDO.getPrice());
                       prizeDTO.setDescription(prizeDO.getDescription());
                   });

                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    return prizeDTO;

                }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList = activityUserDOList
                .stream()
                .map(auDO -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(auDO.getUserId());
                    userDTO.setUserName(auDO.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
                    return userDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setUserDTOList(userDTOList);
        return activityDetailDTO;
    }

    /**
     * 校验活动是否有效
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if (param == null) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //校验人员id是否存在
        List<Long> userIds = param.getActivityUserList().stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existUserIds=userMapper.selectExistByIds(userIds);
        userIds.forEach(id->{
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants. ACTIVITY_USER_ERROR);
            }
        });

        //校验奖品id是否存在
        List<Long> prizeIds=param.getActivityPrizeList().stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds=prizeMapper.selectExistByIds(prizeIds);
        if(CollectionUtils.isEmpty(existPrizeIds)){
            throw new ServiceException(ServiceErrorCodeConstants. ACTIVITY_USER_ERROR);
        }
        prizeIds.forEach(id->{
            if (!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants. ACTIVITY_PRIZE_ERROR);
            }
        });

        //校验人员和奖品数量是否匹配
        int userAmount=param.getActivityUserList().size();
        long prizeAmount=param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userAmount<prizeAmount) {
            throw new ServiceException(ServiceErrorCodeConstants. USER_PRIZE_AMOUNT_ERROR);
        }

        //校验活动奖品等级
        param.getActivityPrizeList().forEach(prize->{
            if(ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())==null){
                throw new ServiceException(ServiceErrorCodeConstants. ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }
}